﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using Owin;
using System.Diagnostics;
using Microsoft.AspNet.Identity;
using CloudStorageLight.Core;
using System.Security.Claims;
using CloudStorageLight.Core.Web;
using System.Diagnostics.CodeAnalysis;
using System.Net.FtpClient;

namespace CloudStorageLight.Web.Models
{
    public static class BasicAuthenticateUtil
    {
        public static bool DisableLogin { get { return SystemSettings.Instance.DisableLogin; } }

        public async static Task Authenticate(HttpApplication httpApp)
        {

            string authString = httpApp.Request.Headers["Authorization"];
            string clientToken = httpApp.Request.Headers["ClientToken"];

            if (!string.IsNullOrEmpty(authString) && authString.IndexOf("Basic", 0) == 0)
            {
                string encodedString = authString.Substring(6);
                byte[] decodedBytes = Convert.FromBase64String(encodedString);
                string decodedString = new ASCIIEncoding().GetString(decodedBytes);
                string[] arrSplited = decodedString.Split(new char[] { ':' });
                string username = arrSplited[0];
                string password = arrSplited[1];
                if (await AuthenticateUser(username, password, httpApp))
                {
                    var account = BlobService.GetCurrentCloudAccount();
                    if (account.UseClientTokenForIPCheck && !string.IsNullOrEmpty(clientToken))
                    {
                        var clientTokenTicket = ClientToken.Parse(clientToken);
                        if (clientTokenTicket == null || !account.CanAccessIPAddress(clientTokenTicket.InitIPAddress))
                        {
                            if (!account.CanAccessIPAddress(WebUtil.GetIPAddress()))
                            {
                                throw new BlobException(BlobExceptionCode.InvalidIPAddress);
                            }
                            clientToken = null;
                        }
                    }
                    else
                    {
                        if (!account.CanAccessIPAddress(WebUtil.GetIPAddress()))
                        {
                            throw new BlobException(BlobExceptionCode.InvalidIPAddress);
                        }

                    }

                    httpApp.Context.User = new GenericPrincipal(new GenericIdentity(username), null);
                    FormsAuthentication.SetAuthCookie(username, false);

                    if (string.IsNullOrEmpty(clientToken))
                    {
                        var ctoken = new ClientToken { ExpireDate = DateTime.MaxValue, IsuueDate = DateTime.UtcNow, InitIPAddress = WebUtil.GetIPAddress(), UserName = username };
                        httpApp.Response.Headers.Add("ClientToken", ctoken.ToToken());
                    }
                }
                else
                {
                    httpApp.Response.Clear();
                    httpApp.Response.StatusCode = 401;
                    httpApp.Response.StatusDescription = "Unauthorized";
                    httpApp.Response.Write("<html><head><title>401 Unauthorized</title></head><body><h1>401 Access Denied</h1></body></html>");
                }
            }

            //authString = httpApp.Request.QueryString["Auth"];
            //if (!string.IsNullOrEmpty(authString))
            //{
            //    try
            //    {
            //        using (var svc = new BlobService())
            //        {
            //            var authTicket = svc.GetAuthTicket(authString);
            //            if (authTicket.ExpireDate > DateTime.UtcNow && httpApp.Request.Url.AbsolutePath.EndsWith(authTicket.Path))
            //            {
            //                httpApp.Context.User = new GenericPrincipal(new GenericIdentity(authTicket.UserName), null);
            //                svc.RenewAuthTicket(authTicket);
            //            }

            //        }
            //    }
            //    catch (Exception ex)
            //    {
            //        Debug.WriteLine(ex.ToString());
            //    }
            //}
        }

        public static void AddUnauthorizedHeader(HttpApplication httpApp)
        {
            if (DisableLogin)
            {
                httpApp.Response.AppendHeader("WWW-Authenticate", "Negotiate");
                httpApp.Response.AppendHeader("WWW-Authenticate", "NTLM");

            }
            else
            {
                httpApp.Response.AppendHeader("WWW-Authenticate", "Basic Realm=Authentication");
            }
        }

        private async static Task<bool> AuthenticateUser(string username, string password, HttpApplication httpApp)
        {

            if (string.IsNullOrEmpty(SystemSettings.Instance.AuthMode) || SystemSettings.Instance.AuthMode == "UserPassword")
            {
                var account = BlobService.GetCurrentCloudAccount();

                if (!account.IsFtpAuthentication())
                {
                    var ctx = new HttpContextWrapper(HttpContext.Current);
                    var manager = ctx.GetOwinContext().GetUserManager<ApplicationUserManager>();
                    var user = await manager.FindAsync(username, password);
                    if (user != null && user.EmailConfirmed)
                    {
                        await SignInAsync(httpApp, user, false);
                        return true;

                    }
                    else
                    {
                        return false;
                    }
                }
                else
                {
                    try
                    {
                        var instance = new FtpClient();
                        instance.Host = account.AuthFtpServer;

                        instance.Credentials = new System.Net.NetworkCredential(username, password);
                        instance.EncryptionMode = FtpEncryptionMode.Explicit;
                        instance.ValidateCertificate += (c, e) => { e.Accept = true; };
                        instance.GetListing("/");
                        return true;
                    }
                    catch (FtpCommandException)
                    {
                        return false;
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("AuthMode");
            }

        }


        private async static Task SignInAsync(HttpApplication httpApp, ApplicationUser user, bool isPersistent)
        {
            var manager = httpApp.Context.GetOwinContext().Authentication;
            var uMng = httpApp.Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
            manager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

            var adminAuth = false;
            if (string.Equals(httpApp.Request.QueryString["adminRole"] ,"true", StringComparison.InvariantCultureIgnoreCase))
            {
                adminAuth = true;
                if (!string.IsNullOrEmpty(user.CellPhoneNo) && !string.IsNullOrEmpty(SystemSettings.Instance.MfaCertFile))
                {
                    adminAuth = AuthAdmin(user);
                }

            }
            var identity = await user.GenerateUserIdentityAsync(uMng);
            if (adminAuth) identity.AddClaim(new Claim("AdminAuthenticate", "true"));

            var account = BlobService.GetCurrentCloudAccount();
            double timeout = FormsAuthentication.Timeout.TotalMinutes;
            if (account != null) 
            {
                identity.AddClaim(new Claim("CloudAccount", account.AccountName));
                timeout = account.AuthTicketTimeout.Value;                
            }
            manager.SignIn(new AuthenticationProperties() { IssuedUtc = DateTime.UtcNow, ExpiresUtc = DateTime.UtcNow.AddMinutes(timeout), IsPersistent = isPersistent }, identity);
        }


        [ExcludeFromCodeCoverage]
        private static bool AuthAdmin(ApplicationUser user)
        {
            var items = user.CellPhoneNo.Split(' ');

            // Add call details from the user database.
            PfAuthParams pfAuthParams = new PfAuthParams();
            pfAuthParams.Username = user.UserName;
            pfAuthParams.Phone = items.Length > 1 ? items[1] : items[0];
            if (items.Length >= 2) pfAuthParams.CountryCode = items[0];
            pfAuthParams.Mode = pf_auth.MODE_STANDARD;

            pfAuthParams.CertFilePath = SystemSettings.Instance.MfaCertFile;

            // Perform phone-based authentication
            int callStatus;
            int errorId;

            return pf_auth.pf_authenticate(pfAuthParams, out callStatus, out errorId);

        }


        public static BlobUser GetCurrentUser()
        {
            AppBlobUser user;
            var account = BlobService.GetCurrentCloudAccount();
            if (account == null) throw new BlobException(BlobExceptionCode.NotAuthorization);
            if (System.Web.HttpContext.Current.User.Identity.IsAuthenticated)
            {
                if (!account.CanAccess(System.Web.HttpContext.Current.User.Identity.Name)) throw new BlobException(BlobExceptionCode.NotAuthorization);

                if (SystemSettings.Instance.MultiDomain)
                {
                    var claims = ((System.Security.Claims.ClaimsIdentity)System.Web.HttpContext.Current.User.Identity).Claims;
                    var cloudAccountClaim = claims.FirstOrDefault(x => x.Type == "CloudAccount");
                    if (cloudAccountClaim == null || account.AccountName != cloudAccountClaim.Value) throw new BlobException(BlobExceptionCode.NotAuthorization);
                }
            }
            user = new AppBlobUser(System.Web.HttpContext.Current.User);

            return user;

        }
    }

}